經過27天的開發與測試,我們的智慧文檔分析系統已經具備完整功能。
但要真正為使用者提供服務,我們還需要將應用部署到生產環境。
今天我們將學習如何使用AWS的各種服務,將應用程式安全、穩定地部署到雲端,並建立監控與維護機制。
延續上一天的專案
是時候把一切集大成了
使用者請求
    ↓
CloudFront (CDN)
    ↓
Application Load Balancer
    ↓
ECS Fargate / App Runner
    ↓
├─ Bedrock (AI推論)
├─ Textract (文字識別)
├─ S3 (檔案儲存)
└─ RDS/DynamoDB (資料)
    ↓
CloudWatch (監控與日誌)
部署考量
Solution 1 : AWS app runner AWS app runner
特色 :
使用場景
中小型應用
快速上線需求
團隊規模較小
Solution 2 : ECS Fargate
使用場景
需要更多控制權
複雜的微服務架構
已有容器化經驗
Solution 3 : Lambda + API Gateway
使用場景
間歇性流量
API服務
成本敏感型專案
我們這裡展示 App runner 部署
這裡我們需要建立 Dockerfile
# 使用官方Python映像
FROM python:3.11-slim
# 設定工作目錄
WORKDIR /app
# 安裝系統依賴
RUN apt-get update && apt-get install -y \
    gcc \
    && rm -rf /var/lib/apt/lists/*
# 複製requirements
COPY requirements.txt .
# 安裝Python依賴
RUN pip install --no-cache-dir -r requirements.txt
# 複製應用程式碼
COPY . .
# 建立非root使用者
RUN useradd -m -u 1000 appuser && \
    chown -R appuser:appuser /app
USER appuser
# 暴露端口
EXPOSE 8501
# 健康檢查
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \
    CMD curl -f http://localhost:8501/_stcore/health || exit 1
# 啟動應用
CMD ["streamlit", "run", "app.py", \
     "--server.port=8501", \
     "--server.address=0.0.0.0", \
     "--server.headless=true", \
     "--server.enableCORS=false", \
     "--server.enableXsrfProtection=true"]
config.py
import os
from dataclasses import dataclass
@dataclass
class Config:
    """應用程式配置"""
    # AWS配置
    AWS_REGION: str = os.getenv('AWS_REGION', '<你的 region>')
    S3_BUCKET: str = os.getenv('S3_BUCKET', 'my-doc-bucket')
    
    # Bedrock配置
    BEDROCK_MODEL_ID: str = os.getenv(
        'BEDROCK_MODEL_ID',
        'anthropic.claude-3-5-sonnet-20241022-v2:0'
    )
    
    # 應用配置
    MAX_FILE_SIZE: int = int(os.getenv('MAX_FILE_SIZE', 10 * 1024 * 1024))  # 10MB
    UPLOAD_TIMEOUT: int = int(os.getenv('UPLOAD_TIMEOUT', 300))  # 5分鐘
    
    # 監控配置
    LOG_LEVEL: str = os.getenv('LOG_LEVEL', 'INFO')
    ENABLE_METRICS: bool = os.getenv('ENABLE_METRICS', 'true').lower() == 'true'
    
    # 安全配置
    ALLOWED_ORIGINS: list = os.getenv(
        'ALLOWED_ORIGINS',
        '*'
    ).split(',')
config = Config()
對 docker 相關的知識以及容器化不夠熟悉的可以參考 Docker官網
並參考 AWS ECR
aws ecr create-repository \
    --repository-name document-analyzer \
    --region <你的region>
# 登入ECR
aws ecr get-login-password --region <你的Region> | \
    docker login --username AWS --password-stdin \
    <account-id>.dkr.ecr.<你的Region>.amazonaws.com
# 建構映像
docker build -t document-analyzer:latest .
# 標記映像
docker tag document-analyzer:latest \
    <account-id>.dkr.ecr.<你的Region>.amazonaws.com/document-analyzer:latest
# 推送映像
docker push <account-id>.dkr.ecr.<你的Region>.amazonaws.com/document-analyzer:latest
apprunner.yml
version: 1.0
runtime: python311
build:
  commands:
    build:
      - pip install -r requirements.txt
run:
  runtime-version: 3.11.0
  command: streamlit run app.py --server.port=8080
  network:
    port: 8080
    env: APP_PORT
  env:
    - name: AWS_REGION
      value: <你的Region>
    - name: LOG_LEVEL
      value: INFO
# 建立服務
aws apprunner create-service \
    --service-name document-analyzer \
    --source-configuration '{
        "ImageRepository": {
            "ImageIdentifier": "<account-id>.dkr.ecr.<你的region>.amazonaws.com/document-analyzer:latest",
            "ImageConfiguration": {
                "Port": "8501",
                "RuntimeEnvironmentVariables": {
                    "AWS_REGION": "<你的Region>",
                    "S3_BUCKET": "my-doc-bucket"
                }
            },
            "ImageRepositoryType": "ECR"
        },
        "AutoDeploymentsEnabled": true,
        "AuthenticationConfiguration": {
            "AccessRoleArn": "arn:aws:iam::<account-id>:role/AppRunnerECRAccessRole"
        }
    }' \
    --instance-configuration '{
        "Cpu": "1 vCPU",
        "Memory": "2 GB"
    }' \
    --health-check-configuration '{
        "Protocol": "HTTP",
        "Path": "/_stcore/health",
        "Interval": 10,
        "Timeout": 5,
        "HealthyThreshold": 1,
        "UnhealthyThreshold": 5
    }' \
    --auto-scaling-configuration-arn \
        "arn:aws:apprunner:<你的Region>:<account-id>:autoscalingconfiguration/DefaultConfiguration/1/00000000000000000000000000000001"
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "build.apprunner.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}
policy
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ecr:GetDownloadUrlForLayer",
                "ecr:BatchGetImage",
                "ecr:BatchCheckLayerAvailability",
                "ecr:GetAuthorizationToken"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "bedrock:InvokeModel"
            ],
            "Resource": "arn:aws:bedrock:*::foundation-model/*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "s3:GetObject",
                "s3:PutObject",
                "s3:DeleteObject"
            ],
            "Resource": "arn:aws:s3:::my-doc-bucket/*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "textract:DetectDocumentText",
                "textract:AnalyzeDocument"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "arn:aws:logs:*:*:*"
        }
    ]
}
其他部分關於成本以及監控,在先前已有展示,讀者可以自行練習